package com.heaven.service.minio;

import com.heaven.exception.RRException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class MinIoService {
    @Resource
    private MinioClient minioClient;
    @Value("${minio.bucket1}")
    public String bucketName;

    /**
     *  获取资源的外部链接
     * @param bucket bucket名
     * @param objectName 存储对象名
     * @param timeout 过期时间
     * @param unit 时间单位
     * @return 外部访问链接地址🔗
     */
    public String getObjectUrl(final String bucket, final String objectName, final int timeout, final TimeUnit unit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucket)
                    .object(objectName)
                    .expiry(timeout, unit)
                    .build()
            );
        } catch (Exception e) {
            throw new RRException("获取minio链接异常-"+e.getMessage());
        }
    }

    /**
     * 获取 minio 中的外部访问链接
     * @param pattern 数据库中文件存储模式 {bucketName}:{objectName}
     * @return 文件访问链接
     */
    public String getLink(String pattern) {
        String[] split = pattern.split(":");
        return getObjectUrl(split[0], split[1], 1, TimeUnit.DAYS);
    }

    /**
     * 检查文件夹是否存在
     * @param bucket bucket
     * @param pathPrefix object的问价前缀(必须从第一位开始)
     * @return true: 存在；false:不存在
     */
   public boolean isDirExist(String bucket, String pathPrefix){
        Iterable<Result<Item>>  results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucket)
                        .prefix(pathPrefix)
                        .maxKeys(1)
                        .build());
        return results.iterator().hasNext();
   }

    /**
     * 检测 object 是否存在
     * @param bucket 桶
     * @param objectName 完整的objectName
     * @return 存在: true； 不存在: false
     */
   public boolean isObjectExist(String bucket, String objectName){
       try (InputStream stream = minioClient.getObject(
               GetObjectArgs.builder()
                       .bucket(bucket)
                       .object(objectName)
                       .length(1L)
                       .build())) {
           return stream != null;
       }  catch (ErrorResponseException e) {
           if(e.errorResponse().code().equals("NoSuchKey")){
               return false;
           }
           throw new RuntimeException(e);
       } catch (Exception e){
           e.printStackTrace();
           throw new RRException("minio未知异常");
       }
   }


    /**
     * 上传文件到minio
     * @param bucket 存储桶
     * @param objectName 存储名称
     * @param is 要存储的文件流
     * @param size 文件大小 B
     * @param contentType 文件类型
     */
   public void putObject(String bucket, String objectName, InputStream is, long size, String contentType){
       try {
           ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                   PutObjectArgs.builder().bucket(bucket).object(objectName).stream(
                                   is, size, -1
                           )
                           .contentType(contentType)
                           .build()
           );
           if(objectWriteResponse != null) {
               log.info("{} up to minio server successfully! tag: {}, version: {}", objectName, objectWriteResponse.etag(), objectWriteResponse.versionId());
           } else {
               log.error("{} up to minio server failed !!!", objectName);
           }
       } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                XmlParserException e) {
           e.printStackTrace();
           log.error("推送流到minio服务器异常:{}, 类型:{}", e.getMessage(), e.getClass());
           throw new RRException("推送流到minio服务器异常");
       }
   }

    /**
     * 上传文件到minio
     * @param bucket 存储桶
     * @param objectName 存储名称
     * @param is 要存储的文件流
     * @param size 文件大小 B
     * @param contentType 文件类型
     */
    public void putObject(String bucket, String objectName, InputStream is, long size, long partSize, String contentType){
        try {
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucket).object(objectName).stream(
                                    is, size, partSize
                            )
                            .contentType(contentType)
                            .build()
            );
            if(objectWriteResponse != null) {
                log.info("{} up to minio server successfully! tag: {}, version: {}", objectName, objectWriteResponse.etag(), objectWriteResponse.versionId());
            } else {
                log.error("{} up to minio server failed !!!", objectName);
            }
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            e.printStackTrace();
            log.error("推送流到minio服务器异常:{}, 类型:{}", e.getMessage(), e.getClass());
            throw new RRException("推送流到minio服务器异常");
        }
    }

    /**
     * 删除 dirPrefix 下所有的文件
     * @param bucket 存储桶
     * @param dirPrefix 文件夹前缀(文件夹)
     */
   public void delDir(String bucket, String dirPrefix){
       List<DeleteObject> deleteObjectList = new ArrayList<>(); //存放删除分块的列表
       Iterable<Result<Item>> objectsIterable = minioClient.listObjects(
               ListObjectsArgs.builder()
                       .bucket(bucket)
                       .prefix(dirPrefix)
                       .recursive(true).build());

       try {
           for (Result<Item> itemResult : objectsIterable) {
               Item item = itemResult.get();
               if (!item.isDir()) {
                   deleteObjectList.add(new DeleteObject(item.objectName()));
               }
           }
           Iterable<Result<DeleteError>> delResults = minioClient.removeObjects(
                   RemoveObjectsArgs.builder().bucket(bucket).objects(deleteObjectList).build());
           for (Result<DeleteError> result : delResults) {
               DeleteError error = result.get();
               log.error("Error in deleting object {}; {}", error.objectName(), error.message());
               // TODO 处理没有删干净的缓存文件

               throw new RRException("minio 删除文件异常");
           }
           log.info("dir {} clear~~", dirPrefix);
       }catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
               InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
               XmlParserException e) {
           throw new RuntimeException(e);
       }
   }

    /**
     * 列举出 prefixPath 下所有的objectName
     * @param bucket bucket
     * @param prefixPath 文件夹前缀
     * @return prefixPath 下 object列表
     */
   public List<String> listObjets(String bucket, String prefixPath) {
       try {
           Iterable<Result<Item>> objectsIterable = minioClient.listObjects(
                   ListObjectsArgs.builder()
                           .bucket(bucket)
                           .prefix(prefixPath)
                           .recursive(true).build());
           List<String> ans = new ArrayList<>();
           for (Result<Item> itemResult : objectsIterable) {
               Item item = itemResult.get();
               ans.add(item.objectName());
           }
           return ans;
       } catch (Exception e) {
           e.printStackTrace();
           throw new RRException("列举object异常");
       }
   }

   public StatObjectResponse getObjectInfo(String bucket, String objectName) {
       try {
            return minioClient.statObject(
                   StatObjectArgs.builder()
                           .bucket(bucket)
                           .object(objectName)
                           .build());
       } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                XmlParserException e) {
           e.printStackTrace();
           throw new RRException("获取文件信息异常-"+e.getMessage());
       }
   }

   public void uploadLocalFile(String bucket, String objectName, String fileName) {
       log.info("start to upload local file {} to minio server", fileName);
       try {
           minioClient.uploadObject(
                   UploadObjectArgs.builder()
                           .bucket(bucket)
                           .object(objectName)
                           .filename(fileName)
                           .build());
           log.info("upload local file to minio server successfully, object--{}", objectName);
       } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                XmlParserException e) {
           throw new RuntimeException(e);
       }
   }
}
